{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Drawing a transformed object"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Scale\n",
    "\n",
    "If you want to scale a 3D object, you just have to multiply each of the vertices of your object by a scalar or constant value. That will do it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Translation (or position movement)\n",
    "\n",
    "If you want to make a translation (position moving) for an object, you just have to add or subtract a vector to your object. For example, add (-1, 0, 0) will move your object one unit in the negative-x direction."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Composing vector transformations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image.png](images/1.scaleAndTranslateleftAsSequence.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can think of the result as a new machine that does the work of both the original functions in one step. This “welding” of functions can be done in code as well. We can write a general-purpose compose function that takes two Python functions (for vector transformations, for instance) and returns a new function, which is their composition:\n",
    "\n",
    "```python\n",
    "def compose(f1,f2):\n",
    "    def new_function(input):\n",
    "        return f1(f2(input))\n",
    "    return new_function\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A replacement for this could be a `scale_by` function that returns a scaling transformation for a specified scalar:\n",
    "\n",
    "```python\n",
    "def scale_by(scalar):\n",
    "    def new_function(v):\n",
    "        return scale(scalar, v)\n",
    "    return new_function\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is a programmatic machine that behaves identically but is invoked differently; \n",
    "\n",
    "for instance, `scale_by(s)(v)` gives the same result as `scale(s,v)` for any inputs `s` and `v`. \n",
    "\n",
    "The advantage is that `scale(...)` and `add(...)` accept different kinds of arguments, so the resulting functions, `scale_by(s)` and `translate_by(w)`, are interchangeable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Rotating an object about an axis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You already saw how to do rotations in 2D in chapter 2: you convert the Cartesian coordinates to polar coordinates, increase or decrease the angle by the rotation factor, and then convert back. Even though this is a 2D trick, it is helpful in 3D because all 3D vector rotations are, in a sense, isolated in planes.\n",
    "\n",
    "Picture, for instance, a single point in 3D being rotated about the z-axis. Its x- and y-coordinates change, but its z-coordinate remains the same. If a given point is rotated around the z-axis, it stays in a circle with a constant z-coordinate, regardless of the rotation angle.\n",
    "\n",
    "What this means is that we can rotate a 3D point around the z-axis by holding the z-coordinate constant and applying our 2D rotation function only to the x- and y-coordinates."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The old function is:\n",
    "\n",
    "```python\n",
    "def rotate2d(angle, vector):\n",
    "    l,a = to_polar(vector)\n",
    "    return to_cartesian((l, a+angle))\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The new function is:\n",
    "\n",
    "```python\n",
    "def rotate_z(angle, vector):\n",
    "    x,y,z = vector\n",
    "    new_x, new_y = rotate2d(angle, (x,y))\n",
    "    return new_x, new_y, z\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Continuing to think in the functional programming paradigm, we can curry this function. Given any angle, the curried version produces a vector transformation that does the corresponding rotation:\n",
    "\n",
    "```python\n",
    "def rotate_z_by(angle):\n",
    "    def new_function(v):\n",
    "        return rotate_z(angle,v)\n",
    "    return new_function\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Inventing your own geometric transformations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remember, the only requirement for a 3D vector transformation is that it accepts a single 3D vector as input and returns a new 3D vector as its output."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For our teapot, let’s modify one coordinate at a time. This function stretches vectors by a (hard-coded) factor of four, but only in the x direction:\n",
    "\n",
    "```python\n",
    "def stretch_x(vector):\n",
    "    x,y,z = vector\n",
    "    return (4.*x, y, z)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image.png](images/2.AteapotStretchedAlongTheXaxis.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Another way of doing this:\n",
    "\n",
    "```python\n",
    "def cube_stretch_z(vector):\n",
    "    x,y,z = vector\n",
    "    return (x, y*y*y, z)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image.png](images/3.cubeStretching.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we selectively add two of the three coordinates in the formula for the transformation, for instance the x and y coordinates, we can cause the teapot to slant.\n",
    "\n",
    "```python\n",
    "def slant_xy(vector):\n",
    "    x,y,z = vector\n",
    "    return (x+y, y, z)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![image.png](images/4.slant.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The point is not that any one of these transformations is impor tant or useful, but that any mathematical transformation of the vectors constituting a 3D model have some geometric con sequence on the appearance of the model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 4.2\n",
    "\n",
    "Render the teapot translated by 20 units in the negative z direction. What does the resulting image look like?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: pygame in /home/yingshaoxo/miniconda3/envs/cling/lib/python3.9/site-packages (2.0.1)\n",
      "Collecting pyopengl\n",
      "  Using cached PyOpenGL-3.1.5-py3-none-any.whl (2.4 MB)\n",
      "Installing collected packages: pyopengl\n",
      "Successfully installed pyopengl-3.1.5\n"
     ]
    }
   ],
   "source": [
    "!pip install pygame\n",
    "!pip install pyopengl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transforms import *\n",
    "from teapot import *\n",
    "from draw_model import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'quit' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-19-aaa4d138e629>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdraw_model\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpolygon_map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtranslate_by\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mload_triangles\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/CS/notebooks/play-with-math/Vectors and graphics/4. Transforming vectors and graphics/draw_model.py\u001b[0m in \u001b[0;36mdraw_model\u001b[0;34m(faces, color_map, light, glRotatefArgs, get_matrix)\u001b[0m\n\u001b[1;32m     51\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mevent\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     52\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mQUIT\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 53\u001b[0;31m                 \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     54\u001b[0m                     \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     55\u001b[0m                     \u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'quit' is not defined"
     ]
    }
   ],
   "source": [
    "draw_model(polygon_map(translate_by((0,0,-20)), load_triangles()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 4.3\n",
    "\n",
    "![image.png](images/5.exercise4.3.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 4.4\n",
    "\n",
    "First apply translate1left to the teapot and then apply scale2. How is the result different from the opposite order of composition? Why?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'quit' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-25-376f97e3856b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m draw_model(\n\u001b[0m\u001b[1;32m      2\u001b[0m     \u001b[0mpolygon_map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mscale_by\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpolygon_map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtranslate_by\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mload_triangles\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m )\n",
      "\u001b[0;32m~/CS/notebooks/play-with-math/Vectors and graphics/4. Transforming vectors and graphics/draw_model.py\u001b[0m in \u001b[0;36mdraw_model\u001b[0;34m(faces, color_map, light, glRotatefArgs, get_matrix)\u001b[0m\n\u001b[1;32m     51\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mevent\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     52\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mQUIT\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 53\u001b[0;31m                 \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     54\u001b[0m                     \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     55\u001b[0m                     \u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'quit' is not defined"
     ]
    }
   ],
   "source": [
    "draw_model(\n",
    "    polygon_map(scale_by(2), polygon_map(translate_by((-1,0,0)), load_triangles()))\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'quit' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-26-34deba63d3f7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m draw_model(\n\u001b[0m\u001b[1;32m      2\u001b[0m     \u001b[0mpolygon_map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtranslate_by\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpolygon_map\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mscale_by\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mload_triangles\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m )\n",
      "\u001b[0;32m~/CS/notebooks/play-with-math/Vectors and graphics/4. Transforming vectors and graphics/draw_model.py\u001b[0m in \u001b[0;36mdraw_model\u001b[0;34m(faces, color_map, light, glRotatefArgs, get_matrix)\u001b[0m\n\u001b[1;32m     51\u001b[0m         \u001b[0;32mfor\u001b[0m \u001b[0mevent\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     52\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mevent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mQUIT\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 53\u001b[0;31m                 \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     54\u001b[0m                     \u001b[0mpygame\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     55\u001b[0m                     \u001b[0mquit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'quit' is not defined"
     ]
    }
   ],
   "source": [
    "draw_model(\n",
    "    polygon_map(translate_by((-1,0,0)), polygon_map(scale_by(2), load_triangles()))\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is that the teapot is still twice as large as the original, but this one is translated further to the left. This is because when a scaling factor of 2 is applied after a translation, the distance of the translation doubles as well. You can convince yourself by running the source files scale_translate_teapot.py and translate_scale_teapot .py and comparing the results."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise 4.9\n",
    "\n",
    "Write a curried function by yourself."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "def operate(scalar, val):\n",
    "    return scalar * val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "def operate_by(scalar):\n",
    "    def new_func(val):\n",
    "        return operate(scalar, val)\n",
    "    return new_func"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = operate_by(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "24"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f(8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
